package org.apache.derby.iapi.services.monitor;

import org.apache.derby.shared.common.i18n.BundleFinder;
import org.apache.derby.shared.common.info.JVMInfo;

import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

/**
 * @author ex_chenzy59
 * @since 2025/3/13
 *
 */
public final class MessageService {
    private static final Locale EN = Locale.of("en", "US");
    private static final String CLIENT_MESSAGES = "clientmessages";
    private static final String TOOLS_MESSAGES = "toolsmessages";
    private static final String SYSINFO_MESSAGES = "sysinfoMessages";
    private static BundleFinder finder;
    public static String getTextMessage(String messageId, Object... arguments) {
        try {
            return formatMessage(getBundle(messageId), messageId, arguments, true);
        } catch (MissingResourceException mre) {
            // message does not exist in the requested locale or the default locale.
            // most likely it does exist in our fake base class _en, so try that.
        } catch (ShutdownException se) {
        }
        return formatMessage(getBundleForLocale(EN, messageId), messageId, arguments, false);
    }
    public static String formatMessage(ResourceBundle bundle, String messageId, Object[] arguments, boolean lastChance) {

        if (arguments == null)
            arguments = new Object[0];

        if (bundle != null) {

            try {
                messageId = bundle.getString(messageId);

                try {
                    return MessageFormat.format(messageId, arguments);
                }
                catch (IllegalArgumentException iae) {
                }
                catch (NullPointerException npe) {
                    //
                    //null arguments cause a NullPointerException.
                    //This improves reporting.
                }

            } catch (MissingResourceException mre) {
                // caller will try and handle the last chance
                if (lastChance)
                    throw mre;
            }
        }

        if (messageId == null)
            messageId = "UNKNOWN";


        StringBuffer sb = new StringBuffer(messageId);

        int len = arguments.length;
        if (len > 0)
            sb.append(" : ");

        for (int i=0; i < len; i++) {
            // prepend a comma to all but the first
            if (i > 0)
                sb.append(", ");

            sb.append('[');
            sb.append(i);
            sb.append("] ");
            if (arguments[i] == null)
                sb.append("null");
            else
                sb.append(arguments[i].toString());
        }


        return sb.toString();
    }
    private static ResourceBundle getBundle(String messageId) {
        ResourceBundle bundle = null;
        if (finder != null){
            bundle = finder.getBundle(messageId);
        }
        if (bundle == null) {
            bundle = MessageService.getBundleForLocale(Locale.getDefault(), messageId);
        }

        return bundle;
    }
    private static ResourceBundle getBundle(String resource, Locale locale)
    {
        ResourceBundle retval = null;

        try {
            retval = lookupBundle(localizeResourceName(resource, locale.toString()), locale);
        } catch (MissingResourceException mre) {}

        // just try the language. it's better than nothing.
        if (retval == null)
        {
            try {
                retval = lookupBundle(localizeResourceName(resource, locale.getLanguage()), locale);
            } catch (MissingResourceException mre) {}
        }

        return retval;
    }
    private static ResourceBundle lookupBundle(String resource, Locale locale){
        return JVMInfo.isModuleAware() ? lookupBundleInModule(resource, locale) : ResourceBundle.getBundle(resource, locale);
    }
    public static ResourceBundle lookupBundleInModule(String resource, Locale locale){
        String moduleName;
        boolean useEnglish = locale.getLanguage().equals(EN.getLanguage());
        boolean useEngineModule = false;
        boolean localizationModule = false;

        //
        // The message localizations should be in one of the following modules:
        //
        //   engine jar
        //   client jar
        //   tools jar
        //   locale-specific message jar
        //

        if (resource.contains(CLIENT_MESSAGES)) { moduleName = ModuleUtil.CLIENT_MODULE_NAME; }
        else if (resource.contains(SERVER_MESSAGES)) { moduleName = ModuleUtil.SERVER_MODULE_NAME; }
        else if (resource.contains(TOOLS_MESSAGES) || resource.contains(SYSINFO_MESSAGES))
        {
            if (useEnglish){ moduleName = ModuleUtil.TOOLS_MODULE_NAME; }
            else
            {
                moduleName = ModuleUtil.localizationModuleName(locale.toString());
                localizationModule = true;
            }
        }
        else // must be engine messages
        {
            if (useEnglish)
            {
                moduleName = ModuleUtil.ENGINE_MODULE_NAME;
                useEngineModule = true;
            }
            else
            {
                moduleName = ModuleUtil.localizationModuleName(locale.toString());
                localizationModule = true;
            }
        }

        Module messageModule = ModuleUtil.derbyModule(moduleName);
        if (messageModule == null)
        {
            if (localizationModule)
            {
                // retry with just the language as the suffix of the localization module
                moduleName = ModuleUtil.localizationModuleName(locale.getLanguage());
                messageModule = ModuleUtil.derbyModule(moduleName);
            }
        }
        if (messageModule == null)
        {
            return null;
        }

        // first try with whole locale string
        ResourceBundle result = lookupBundleInModule
                (messageModule, resource, locale.toString(), useEnglish, useEngineModule);

        // if that fails, just use the language code without the country code
        if ( result == null)
        {
            result = lookupBundleInModule
                    (messageModule, resource, locale.getLanguage(), useEnglish, useEngineModule);
        }

        return result;
    }
    private static String localizeResourceName(String original, String localeName)
    {
        if (
                (original == null) ||
                        (original.contains(CLIENT_MESSAGES)) ||
                        (original.contains(SERVER_MESSAGES))
        )
        { return original; }


        // American English messages are not re-located to a subdirectory
        if (EN.toString().equals(localeName))
        { return original; }

        int lastDotIdx = original.lastIndexOf('.');
        String retval =
                original.substring(0, lastDotIdx + 1) +
                        LOCALE_STUB + localeName +
                        original.substring(lastDotIdx, original.length());

        return retval;
    }
    public static ResourceBundle getBundleForLocale(Locale locale, String msgId) {
        try {
            return MessageService.getBundleWithEnDefault("org.apache.derby.loc.m"+hashString50(msgId), locale);
        } catch (MissingResourceException mre) {
        }
        return null;
    }
    public static ResourceBundle getBundleWithEnDefault(String resource, Locale locale) {
        ResourceBundle retval = null;

        retval = getBundle(resource, locale);

        if (retval == null)
        {
            //
            // This can happen if the database territory overrides
            // the default Locale, but the territory isn't supported.
            // Try the default Locale.
            //
            Locale defaultLocale = Locale.getDefault();

            if (!defaultLocale.equals(locale))
            {
                retval = getBundle(resource, defaultLocale);
            }
        }

        //
        // Ok, fallback on English, the localization bundled into the base Derby jars.
        // This throws MissingResourceException if the situation is completely
        // confused.
        //
        if (retval == null)
        {
            retval = lookupBundle(resource, EN);
        }

        return retval;
    }
}
